﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AzureGames.Data;
using AzureGames.Domain.Contracts;
using AzureGames.Domain.Models;
using AzureGames.Model;

namespace AzureGames.Domain.Handlers
{
    public class UpdateBoard
    {
        private readonly IBoardRepository _repository;
        private readonly IGameDefinitionRepository _gamedefinitionRepository;
        private readonly IUserStatisticsRepository _userstatisticsRepository;
        private readonly IGameStatisticsRepository _gamestatisticsRepository;
        private readonly IPlayerRepository _playerRepository;
        private readonly INotificationRepository _notificationRepository;

        public UpdateBoard(IGameDefinitionRepository gamedefinitionRepository, IBoardRepository gameBoardRepository, IUserStatisticsRepository userstatisticsRepository, IGameStatisticsRepository gamestatisticsRepository, IPlayerRepository playerRepository,  INotificationRepository notificationRepository)
        {
            _repository = gameBoardRepository;            
            _playerRepository = playerRepository;
            _gamestatisticsRepository = gamestatisticsRepository;
            _userstatisticsRepository = userstatisticsRepository;
            _notificationRepository = notificationRepository;
            _gamedefinitionRepository = gamedefinitionRepository;
        }

        public virtual bool Execute(Model.Board board)
        {
            try
            {
                var players = board.PlayerIDs.Select(pId => _playerRepository.GetPlayer(pId)).ToArray();
                
                // TODO: there is a chance of race condition here if both players 
                //       try to update the game board at the same time
                var currentBoard = _repository.GetGameBoard(board.ID);
                var gameDefinition = _gamedefinitionRepository.GetGameDefinition(board.GameDefinitionID);

                if (board.State != currentBoard.State)
                {
                    // TODO: rethink this.  it would be better if the Model.Board objects are stored indefinitely
                    //       and statistics are built from them on startup of the webrole.  A listener could then be
                    //       created to maintain the totals more accurately.  This should work for now...                    
                    var gameType = players.Any(p=>p is Model.AIPlayer) ? Model.Statistics.GameType.SinglePlayer : Model.Statistics.GameType.MultiplePlayer;
                    
                    foreach(var player in players)
                    {
                        _userstatisticsRepository.AddUserStatistic(board.GameDefinitionID, gameType, board.CreatedDate, player.ID, board.State);
                        _userstatisticsRepository.RemoveUserStatistic(board.GameDefinitionID, gameType, board.CreatedDate, player.ID, currentBoard.State);
                    }

                    _gamestatisticsRepository.AddGameStatistic(board.GameDefinitionID, gameType, board.CreatedDate, board.State);
                    _gamestatisticsRepository.RemoveGameStatistic(board.GameDefinitionID, gameType, board.CreatedDate, currentBoard.State);                    
                }

                var gamePlayers = new List<GamePlayer>(players.Select(p=>new GamePlayer(p)));

                // notify the players that the gameboard has changed
                foreach (var player in players)
                {
                    if (player is Model.User)
                    {
                        // notify players of change by adding message to queue
                        _notificationRepository.PostNotification(new Notification
                        {
                            GameDefinitionID = board.GameDefinitionID,
                            Data = board,
                            Message = "UpdateBoard",
                            ID = Guid.NewGuid(),
                            PlayerID = player.ID,
                            BoardID = board.ID,
                            State = Notification.STATE.Pending
                        });
                    }
                }

                // TODO: a more clever way needs to be done to update the gameboard... 
                //       maybe parse part of the json                  



                // TODO: some form of validation should be performed as it would be very easy at the moment
                //       for someone to cheat by modifying the gameboard JSON
                //       maybe we will record a history of JSON or call out to a class to validate the move
                //       or use node.js to receive and validate and then to call this method if validation 
                //       was successful
                    return _repository.UpdateGameBoard(board);                
            }
            catch (InvalidOperationException e)
            {
                throw new BusinessServicesException("Unable to retrieve game board from database.", e);
            }
        }
    }
}
